home *** CD-ROM | disk | FTP | other *** search
- /*===================================================================
- *
- * FILE:
- * prs2stack.c
- *
- * IDENTIFICATION:
- * $Header: /private/postgres/src/rules/prs2/RCS/prs2stack.c,v 1.4 1991/11/18 22:21:22 mer Exp $
- *
- * DESCRIPTION:
- * This file contains some routines used to implement a loop detection
- * mechanism.
- *
- * The Problem:
- * Sometimes a rule in order to calculate the value of an attribute
- * (that has a LockTypeWrite on it) need to execute a plan.
- * This plan might retrieve the same tuple that caused the
- * activation of the rule in the first place, thus reactivating the rule
- * ans causing a loop.
- * For example, assume the rule:
- * ON retrieve to EMP.salary where EMP.name = "Mike"
- * DO INSTEAD retrieve (EMP.salary) where EMP.name = "Fred"
- * This rule states that if we try to retrieve Mike's salary
- * we must substitute Fred's salary instead.
- * If a user tries to retrieve Mike's salary the above rule will be
- * activated, and it will execute the plan:
- * retrieve (EMP.salary) where EMP.name = "Fred"
- * Lets assume that the planner had decided to use a sequential scan
- * and that Mike's it encounter's Mike's tuple before it encounter's
- * Fred's. In this case the executor will try to retrieve both
- * name and salary of Mike's tuple at the same time (it first
- * retrieves all the attributes needed and *then* checks fo rthe
- * qualification, i.e. to see if name == "Fred".
- * So, as Mike's salary is retrieved, then the rule manager will
- * reactivate the rule above, which will try again to run the plan
- * retrieve (EMP.salary) where EMP.name = "Fred"
- * which will cuase yet another activation of the rule etc....
- *
- * The Solution:
- * Every time we try to calculate the value of a tuple's attribute
- * by activating a rule, we put in a stack the correpsonding rule oid,
- * tuple oid and attribute number.
- * Before doing that though, we first check to see if there is already
- * a similar entry (same ruleId, tuple oid * attribute number) in the
- * stack. If yes, then we have entered a loop.
- * If no, we go ahead and execute the rule. When the rule has finished
- * its execution, we remove the entry from the stack.
- * Note that the length of the stack is proprtional to the depth
- * of the recursion in the ExecMain/rule manager calls.
- *
- * Some Options:
- * Once we have discovered a rule loop we have to decide what to do.
- * Some alternatives:
- * 1) Abort transaction! (not a good solution, as in some cases
- * (as in the example above with Fred's & Mike's salary) the rule
- * is OK, and the loop is created because of the implementation.
- * 2) Ignore this rule and either activate other rules that possibly
- * exist, or use the value as stored in the tuple.
- * 3) Ignore the tuple altogether!
- *
- * MEMORY MANAGEMENT PROBLEMS:
- * Every time we push an item in the stack we first check if we have
- * enough space. If not, we allocate some new space, copty there the
- * contents of the old one and then free the old one.
- * However, as each recursive call of 'prs2Main' changes the memory
- * context (we do that to avoid memory leaks - see comments in prs2main.c)
- * it is possible that we allocate some space under a certain mem cntxt
- * and when we want to reallocate some more we are under another.
- * This of course causes bad things to happen and our beloved postgres
- * dies a horrible death.
- * So I did an extensive study of all the possible alternatives
- * carefully examining all the tradeoff involved, taking into account
- * all possible softawre engineering considerations, and finally after an
- * extensive period of thought (approximately 10 seconds) I decided
- * to create (yes!) yet another memory context specially designed and
- * custom made for the rule stack.
- *
- *===================================================================
- */
-
- #include "tmp/c.h"
- #include "utils/log.h"
- #include "rules/prs2.h"
- #include "nodes/mnodes.h"
- #include "utils/mcxt.h"
-
-
- #define PRS2_INCREMENT_STACKSIZE 1
-
- /*-----
- * this is a local static variable :
- * Rule Stack Memeory Context!
- */
- static GlobalMemory Prs2RuleStackMemoryContext = NULL;
-
- /*-------------------------------------------------------------------
- * prs2RuleStackPush
- *
- * Add a new entry to the rule stack. First check if there is enough
- * stack space. otherwise reallocate some more memory...
- */
- void
- prs2RuleStackPush(p, ruleId, tupleOid, attributeNumber)
- Prs2EStateInfo p;
- ObjectId ruleId;
- ObjectId tupleOid;
- AttributeNumber attributeNumber;
- {
- Prs2Stack temp;
- int newSize;
- MemoryContext oldMemContext;
-
- /*
- * remember the stack pointer always points
- * to the next free stack entry...
- */
- if (p->prs2StackPointer >= p->prs2MaxStackSize) {
- /*
- * we need to allocate some more stack room!
- * switch to `Prs2RuleStackMemoryContext' for all memory
- * allocations
- */
- if (Prs2RuleStackMemoryContext == NULL) {
- elog(WARN,"prs2RuleStackFree: Prs2RuleStackMemoryContext is NULL!");
- }
- oldMemContext =
- MemoryContextSwitchTo((MemoryContext)Prs2RuleStackMemoryContext);
- /*
- * allocate more room & free the old one
- */
- newSize = p->prs2MaxStackSize + PRS2_INCREMENT_STACKSIZE;
- temp = (Prs2Stack) palloc(sizeof(Prs2StackData) * newSize);
- if (temp==NULL) {
- elog(WARN,
- "prs2RuleStackPush: out of memory (from %d to %d entries)",
- p->prs2MaxStackSize, newSize);
- }
- if (p->prs2Stack != NULL) {
- bcopy(p->prs2Stack,
- temp,
- sizeof(Prs2StackData)*p->prs2MaxStackSize);
- pfree(p->prs2Stack);
- }
- p->prs2Stack = temp;
- p->prs2MaxStackSize = newSize;
- /*
- * switch back to the old mem context
- */
- (void) MemoryContextSwitchTo(oldMemContext);
- }
-
- /*
- * there is enough space, go and add the new entry..,
- */
-
- p->prs2Stack[p->prs2StackPointer].ruleId = ruleId;
- p->prs2Stack[p->prs2StackPointer].tupleOid = tupleOid;
- p->prs2Stack[p->prs2StackPointer].attrNo = attributeNumber;
-
- p->prs2StackPointer += 1;
-
- }
-
- /*-------------------------------------------------------------------
- * prs2RuleStackPop
- *
- * Discard the top entry of the stack
- */
- void
- prs2RuleStackPop(p)
- Prs2EStateInfo p;
- {
- /*
- * the stackPointer always points to the first free
- * stack item
- */
- if (p->prs2StackPointer <= 0) {
- elog(WARN, "prsRuleStackPop: Empty rule stack!");
- }
-
- p->prs2StackPointer -= 1;
-
- }
-
- /*-------------------------------------------------------------------
- * prs2RuleStackSearch
- *
- * Search for a stack entry matching the given arguments.
- * Return true if found, false otherwise...
- */
- bool
- prs2RuleStackSearch(p, ruleId, tupleOid, attributeNumber)
- Prs2EStateInfo p;
- ObjectId ruleId;
- ObjectId tupleOid;
- AttributeNumber attributeNumber;
- {
- int i;
-
- /*
- * the stackPointer always points to the first free stack
- * element
- */
- for (i=0; i<p->prs2StackPointer; i++) {
- if (p->prs2Stack[i].ruleId == ruleId &&
- p->prs2Stack[i].tupleOid == tupleOid &&
- p->prs2Stack[i].attrNo == attributeNumber) {
- return(true);
- }
- }
-
- return(false);
- }
-
- /*-------------------------------------------------------------------
- * prs2RuleStackInitialize
- *
- * Intialize the stack.
- */
- Prs2EStateInfo
- prs2RuleStackInitialize()
- {
- Prs2EStateInfo p;
- MemoryContext oldMemContext;
-
- /*
- * switch to `Prs2RuleStackMemoryContext' for all memory
- * allocations
- */
- if (Prs2RuleStackMemoryContext == NULL) {
- Prs2RuleStackMemoryContext = CreateGlobalMemory("*prs2RuleStack*");
- }
- oldMemContext =
- MemoryContextSwitchTo((MemoryContext)Prs2RuleStackMemoryContext);
-
- /*
- * create the stack
- */
- p = (Prs2EStateInfo) palloc(sizeof(Prs2EStateInfoData));
- if (p==NULL) {
- elog(WARN,"prs2RuleStackInitialize: palloc(%ld) failed.\n",
- sizeof(Prs2EStateInfoData));
- }
- p->prs2StackPointer = 0;
- p->prs2MaxStackSize = 0;
- p->prs2Stack = NULL;
-
- /*
- * switch back to the original mem context
- */
- (void) MemoryContextSwitchTo(oldMemContext);
-
- return(p);
- }
-
- /*-------------------------------------------------------------------
- * prs2RuleStackFree
- *
- * Free the memory occupied by the stack.
- */
- void
- prs2RuleStackFree(p)
- Prs2EStateInfo p;
- {
- MemoryContext oldMemContext;
-
- /*
- * switch to `Prs2RuleStackMemoryContext' for all memory
- * deallocations
- */
- if (Prs2RuleStackMemoryContext == NULL) {
- elog(WARN,"prs2RuleStackFree: Prs2RuleStackMemoryContext is NULL!");
- }
- oldMemContext =
- MemoryContextSwitchTo((MemoryContext)Prs2RuleStackMemoryContext);
-
- /*
- * free the Prs2EStateInfo
- */
- if (p!= NULL && p->prs2Stack != NULL) {
- pfree(p->prs2Stack);
- }
- if (p!=NULL)
- pfree(p);
-
- /*
- * switch back to the original mem context
- */
- (void) MemoryContextSwitchTo(oldMemContext);
-
- }
-